home *** CD-ROM | disk | FTP | other *** search
/ PC World 2006 November / PCWorld_2006-11_cd.bin / domacnost a kancelar / findgraph / fgraph.exe / {app} / TestVC / CoolMenu.cpp next >
C/C++ Source or Header  |  2002-08-09  |  24KB  |  806 lines

  1. ////////////////////////////////////////////////////////////////
  2. // 1998 Microsoft Systems Journal. 
  3. // from DiLascia Coolmenu\MBTest\CoolMenu.cpp
  4. //
  5. #include "StdAfx.h"
  6. #include "CoolMenu.h"
  7. #include "Resource.h" 
  8.  
  9. #ifdef _DEBUG
  10. #define new DEBUG_NEW
  11. #undef THIS_FILE
  12. static char THIS_FILE[] = __FILE__;
  13. #endif
  14.  
  15. // helpers -----------------------------------------------------------------------
  16. void PLFillRect(CDC& dc, const CRect& rc, COLORREF color);
  17. void PLDrawEmbossed(CDC& dc, CImageList& il, int i,    CPoint p, BOOL bColor=FALSE);
  18. ////////////////////////////////////////////////////////////////
  19. // Helper functions
  20.  
  21. //
  22. void PLFillRect(CDC& dc, const CRect& rc, COLORREF color)
  23. {
  24.     CBrush brush(color);
  25.     CBrush* pOldBrush = dc.SelectObject(&brush);
  26.     dc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY);
  27.     dc.SelectObject(pOldBrush);
  28. }
  29.  
  30. // This is the magic ROP code used to generate the embossed look for
  31. // a disabled button. It's listed in Appendix F of the Win32 Programmer's
  32. // Reference as PSDPxax (!) which is a cryptic reverse-polish notation for
  33. //
  34. // ((Destination XOR Pattern) AND Source) XOR Pattern
  35. //
  36. // which I leave to you to figure out. In the case where I apply it,
  37. // Source is a monochrome bitmap which I want to draw in such a way that
  38. // the black pixels get transformed to the brush color and the white pixels
  39. // draw transparently--i.e. leave the Destination alone.
  40. //
  41. // black ==> Pattern (brush)
  42. // white ==> Destintation (ie, transparent)
  43. //
  44. // 0xb8074a is the ROP code that does this. For more info, see Charles
  45. // Petzold, _Programming Windows_, 2nd Edition, p 622-624.
  46. //
  47. #define TRANSPARENTROP 0xb8074a
  48.  
  49. //////////////////
  50. // Draw an image with the embossed (disabled) look.
  51. //
  52. //        dc            device context to draw in
  53. //        il            image list containing image
  54. //        i            index of image to draw
  55. //        p            point in dc to draw image at
  56. //    bColor    do color embossing. Default is B/W.
  57. //
  58. void PLDrawEmbossed(CDC& dc, CImageList& il, int i,
  59.     CPoint p, BOOL bColor)
  60. {
  61.     IMAGEINFO info;
  62.     VERIFY(il.GetImageInfo(0, &info));
  63.     CRect rc = info.rcImage;
  64.     int cx = rc.Width();
  65.     int cy = rc.Height();
  66.  
  67.     // create memory dc
  68.     CDC memdc;
  69.     memdc.CreateCompatibleDC(&dc);
  70.  
  71.     // create mono or color bitmap
  72.     CBitmap bm;
  73.     if (bColor)
  74.         bm.CreateCompatibleBitmap(&dc, cx, cy);
  75.     else
  76.         bm.CreateBitmap(cx, cy, 1, 1, NULL);
  77.  
  78.     // draw image into memory DC--fill BG white first
  79.     CBitmap* pOldBitmap = memdc.SelectObject(&bm);
  80.     memdc.PatBlt(0, 0, cx, cy, WHITENESS);
  81.     il.Draw(&memdc, i, CPoint(0,0), ILD_TRANSPARENT);
  82.  
  83.     // This seems to be required. Why, I don't know. ???
  84.     COLORREF colorOldBG = dc.SetBkColor(RGB(255,255,255)); // white
  85.  
  86.     // Draw using hilite offset by (1,1), then shadow
  87.     CBrush brShadow(GetSysColor(COLOR_3DSHADOW));
  88.     CBrush brHilite(GetSysColor(COLOR_3DHIGHLIGHT));
  89.     CBrush* pOldBrush = dc.SelectObject(&brHilite);
  90.     dc.BitBlt(p.x+1, p.y+1, cx, cy, &memdc, 0, 0, TRANSPARENTROP);
  91.     dc.SelectObject(&brShadow);
  92.     dc.BitBlt(p.x, p.y, cx, cy, &memdc, 0, 0, TRANSPARENTROP);
  93.     dc.SelectObject(pOldBrush);
  94.     dc.SetBkColor(colorOldBG);                 // restore
  95.     memdc.SelectObject(pOldBitmap);         // ...
  96. }
  97.  
  98.  
  99. // constants used for drawing-----------------------------------------------------------------------
  100. const CXGAP = 1;                // num pixels between button and text
  101. const CXTEXTMARGIN = 2;        // num pixels after hilite to start text
  102. const CXBUTTONMARGIN = 2;    // num pixels wider button is than bitmap
  103. const CYBUTTONMARGIN = 2;    // ditto for height
  104.  
  105. // DrawText flags
  106. const DT_MYSTANDARD = DT_SINGLELINE|DT_LEFT|DT_VCENTER;
  107.  
  108. // identifies owner-draw data as mine
  109. const LONG MYITEMIDM = MAKELONG(MAKEWORD('m','i'),MAKEWORD('m','0'));
  110. const LONG MYITEMIDD = MAKELONG(MAKEWORD('m','i'),MAKEWORD('d','0'));
  111.  
  112. // private struct: one of these for each owner-draw menu item
  113. struct CMyItemData {
  114.     long        magicNum;        // magic number identifying me
  115.     CString        text;            // item text
  116.     UINT        fType;            // original item type flags
  117.     int            iButton;        // index of button image in image list
  118.     CMyItemData()            { magicNum = MYITEMIDM; }
  119.     BOOL IsMyItemData()        { return magicNum == MYITEMIDM; }
  120. };
  121.  
  122. // private struct: one of these for dropdown buttons WM_NOTIFY
  123. struct CMyDropData {
  124.     UINT        idMenu;            // idr popup menu
  125.     UINT        uFlags;            // default TPM_RIGHTBUTTON
  126.     long        magicNum;        // magic number identifying me
  127.     CMyDropData()            { magicNum = MYITEMIDD; }
  128.     BOOL IsMyDropData()        { return magicNum == MYITEMIDD; }
  129. };
  130.  
  131.  
  132.  
  133.  
  134. IMPLEMENT_DYNAMIC(CCoolMenuManager, CSubclassWnd)
  135.  
  136. CCoolMenuManager::CCoolMenuManager()
  137. {
  138.     m_szButton =CSize(18,18);    // will compute later
  139. }
  140.  
  141. CCoolMenuManager::~CCoolMenuManager()
  142. {
  143.     Destroy();
  144. }
  145.  
  146. //////////////////
  147. // Destroy everything. Called from destructor
  148. //
  149. void CCoolMenuManager::Destroy()
  150. {
  151.     m_fontMenu.DeleteObject();
  152. //TRACE0("ConvertMenuRemove \n");
  153.     while (!m_menuItems.IsEmpty())
  154.     {
  155.         CMyItemData *pmd = (CMyItemData *)m_menuItems.RemoveHead();
  156. //        TRACE1("delete = %s  \n",pmd->text);
  157.         delete pmd;
  158.     }
  159.  
  160.     // iterating all (key, value) pairs
  161.     POSITION pos = m_dropButns.GetStartPosition();
  162.     while (pos!=NULL)
  163.     {
  164.         CMyDropData *pdd;
  165.         WORD  wKey;
  166.         m_dropButns.GetNextAssoc(pos, wKey, (void *&)pdd);
  167.         delete pdd;
  168. //        TRACE1("id=%d  \n",wKey);
  169.     }
  170.  
  171.     m_dropButns.RemoveAll();
  172. }
  173.  
  174. //////////////////
  175. // Call this to install the menu manager. Install(NULL) to un-install.
  176. //
  177. void CCoolMenuManager::Install(CFrameWnd* pFrame, CToolBar  *pToolBar)
  178. {
  179.     ASSERT_VALID(pFrame);
  180.     m_pFrame = pFrame;
  181.     m_pToolBar = pToolBar;
  182.  
  183.  
  184.     AddDropDown(ID_FILE_OPEN, IDR_FILEDROPDOWN,    TPM_LEFTALIGN|TPM_RIGHTBUTTON|TPM_VERTICAL);
  185.     HookWindow(pFrame);   // install message hook
  186. }
  187.  
  188.  
  189. //////////////////
  190. // Call this to add drop down style buttons
  191. //
  192.  
  193. void CCoolMenuManager::AddDropDown(UINT idButton, UINT idMenu, UINT uFlags)
  194. {
  195.     m_pToolBar->GetToolBarCtrl().SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS);    
  196.  
  197.     TBBUTTONINFO tbi;
  198.     tbi.dwMask= TBIF_STYLE;
  199.     tbi.cbSize= sizeof(TBBUTTONINFO);
  200.     m_pToolBar->GetToolBarCtrl().GetButtonInfo(idButton, &tbi);
  201.     tbi.fsStyle |= TBSTYLE_DROPDOWN;
  202.     m_pToolBar->GetToolBarCtrl().SetButtonInfo(idButton, &tbi);
  203.  
  204.  
  205.     CMyDropData *pdd;
  206.     pdd = new CMyDropData;                //   create one
  207.     pdd->idMenu = idMenu;
  208.     pdd->uFlags = uFlags;
  209.     
  210.     m_dropButns.SetAt(idButton, pdd);
  211. }
  212.  
  213.  
  214. /////////////////////////////////////////////////////////////////////////////////////
  215. //////////////////
  216. // Get menu font, creating if needed
  217. //
  218. CFont* CCoolMenuManager::GetMenuFont()
  219. {
  220.     if (!(HFONT)m_fontMenu) {
  221.         NONCLIENTMETRICS info;
  222.         info.cbSize = sizeof(info);
  223.         SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
  224.         VERIFY(m_fontMenu.CreateFontIndirect(&info.lfMenuFont));
  225.     }
  226.     return &m_fontMenu;
  227. }
  228.  
  229. // Helper function to draw justified menu text. If the text contains a TAB,
  230. // draw everything after the tab right-aligned
  231. //
  232. void CCoolMenuManager::DrawMenuText(CDC& dc, CRect rc, CString text,
  233.     COLORREF color)
  234. {
  235.     CString left = text;
  236.     CString right;
  237.     int iTabPos = left.Find('\t');
  238.     if (iTabPos >= 0) {
  239.         right = left.Right(left.GetLength() - iTabPos - 1);
  240.         left  = left.Left(iTabPos);
  241.     }
  242.     dc.SetTextColor(color);
  243.     dc.DrawText(left, &rc, DT_MYSTANDARD);
  244.     if (iTabPos > 0)
  245.         dc.DrawText(right, &rc, DT_MYSTANDARD|DT_RIGHT);
  246. }
  247.  
  248. #ifndef OBM_CHECK
  249. #define OBM_CHECK 32760 // from winuser.h
  250. #endif
  251.  
  252. //////////////////
  253. // Draw 3D checkmark
  254. //
  255. //        dc                device context to draw in
  256. //        rc                rectangle to center bitmap in
  257. //        bSelected    TRUE if button is also selected
  258. //        hbmCheck        Checkmark bitmap to use, or NULL for default
  259. //
  260. BOOL CCoolMenuManager::Draw3DCheckmark(CDC& dc, const CRect& rc,
  261.     BOOL bSelected, HBITMAP hbmCheck)
  262. {
  263.     // get checkmark bitmap if none, use Windows standard
  264.     if (!hbmCheck) {
  265.         CBitmap bm;
  266.         VERIFY(bm.LoadOEMBitmap(OBM_CHECK));
  267.         hbmCheck = (HBITMAP)bm.Detach();
  268.         ASSERT(hbmCheck);
  269.     }
  270.     
  271.     // center bitmap in caller's rectangle
  272.     BITMAP bm;
  273.     ::GetObject(hbmCheck, sizeof(bm), &bm);
  274.     int cx = bm.bmWidth;
  275.     int cy = bm.bmHeight;
  276.     CRect rcDest = rc;
  277.     CPoint p(0,0);
  278.     CSize delta(CPoint((rc.Width() - cx)/2, (rc.Height() - cy)/2));
  279.     if (rc.Width() > cx)
  280.         rcDest = CRect(rc.TopLeft() + delta, CSize(cx, cy));
  281.     else
  282.         p -= delta;
  283.  
  284.     // select checkmark into memory DC
  285.     CDC memdc;
  286.     memdc.CreateCompatibleDC(&dc);
  287.     HBITMAP hOldBM = (HBITMAP)::SelectObject(memdc, hbmCheck);
  288.  
  289.     // set BG color based on selected state
  290.     COLORREF colorOld =
  291.         dc.SetBkColor(GetSysColor(bSelected ? COLOR_MENU : COLOR_3DLIGHT));
  292.     dc.BitBlt(rcDest.left, rcDest.top, rcDest.Width(), rcDest.Height(),
  293.         &memdc, p.x, p.y, SRCCOPY);
  294.     dc.SetBkColor(colorOld);
  295.  
  296.     ::SelectObject(memdc, hOldBM); // restore
  297.  
  298.     // draw pushed-in hilight.
  299.     if (rc.Width() > cx)                // if room:
  300.         rcDest.InflateRect(1,1);    // inflate checkmark by one pixel all around
  301.     dc.DrawEdge(&rcDest, BDR_SUNKENOUTER, BF_RECT);
  302.  
  303.     return TRUE;
  304. }
  305.  
  306. //////////////////
  307. // Draw button form toolbar
  308. //
  309. //        dc                device context to draw in
  310. //      iButton            number in m_wndToolBar
  311. //        p                start point
  312. //        bEnabled        TRUE if button is not disabled
  313. //
  314. void CCoolMenuManager::DrawButton(CDC& dc, int iButton, CPoint p, BOOL bEnabled)
  315. {
  316.     TBBUTTON butn;
  317.     if (m_pToolBar->GetToolBarCtrl().GetButton(iButton, &butn))
  318.     {
  319.         CImageList *ImageList = m_pToolBar->GetToolBarCtrl().GetImageList();
  320.         if (bEnabled)
  321.             ImageList->Draw(&dc, butn.iBitmap, p, ILD_TRANSPARENT);
  322.         else
  323.         {
  324.  
  325.             HICON hIcon = ImageList->ExtractIcon(butn.iBitmap);
  326.             ASSERT(hIcon);
  327.             dc.DrawState(p, CSize(0,0), hIcon, DSS_DISABLED, (HBRUSH)NULL);
  328.             DestroyIcon(hIcon);
  329.  
  330.             // or use DrawEmbossed to draw disabeld button, w/color flag
  331.             // PLDrawEmbossed(dc, *ImageList, butn.iBitmap, p, true);
  332.         }
  333.  
  334.     }
  335. }
  336.  
  337.  
  338. //-----------------------------------------------------------------------------------
  339. //////////////////
  340. // Handle WM_MEASUREITEM on behalf of frame: compute menu item size.
  341. //
  342. BOOL CCoolMenuManager::OnMeasureItem(LPMEASUREITEMSTRUCT lpms)
  343. {
  344.     ASSERT(lpms);
  345.     CMyItemData* pmd = (CMyItemData*)lpms->itemData;
  346.     ASSERT(pmd);
  347.     
  348.     if (lpms->CtlType != ODT_MENU || !pmd->IsMyItemData())
  349.         return FALSE; // not handled by me
  350.  
  351.     if (pmd->fType & MFT_SEPARATOR)
  352.     {
  353.         // separator: use half system height and zero width
  354.         lpms->itemHeight = GetSystemMetrics(SM_CYMENU)>>1;
  355.         lpms->itemWidth  = 0;
  356.  
  357.     } else
  358.     {
  359.  
  360.         // compute size of text: use DrawText with DT_CALCRECT
  361.         CString text = pmd->text;
  362.  
  363.         CWindowDC dc(NULL);    // screen DC--I won't actually draw on it
  364.         CRect rcText(0,0,0,0);
  365.         CFont* pOldFont = dc.SelectObject(GetMenuFont());
  366.         dc.DrawText(text, rcText, DT_MYSTANDARD|DT_CALCRECT);
  367.         dc.SelectObject(pOldFont);
  368.  
  369.         // height of item is just height of a standard menu item
  370.         lpms->itemHeight= max(GetSystemMetrics(SM_CYMENU), rcText.Height());
  371.  
  372.         // width is width of text plus a bunch of stuff
  373.         int cx = rcText.Width();    // text width 
  374.         cx += CXTEXTMARGIN<<1;        // L/R margin for readability
  375.         cx += CXGAP;                // space between button and menu text
  376.         cx += m_szButton.cx<<1;        // button width (L=button; R=empty margin)
  377.         // whatever value I return in lpms->itemWidth, Windows will add the
  378.         // width of a menu checkmark, so I must subtract to defeat Windows. Argh.
  379.         //
  380.         cx -= GetSystemMetrics(SM_CXMENUCHECK)-1;
  381.         lpms->itemWidth = cx;        // done deal
  382.  
  383.         //TRACE1(_T("OnMeasureItem for '%s':"), text); TRACE2(_T("\tw=%d h=%d\n"), lpms->itemWidth, lpms->itemHeight);
  384.     }
  385.     return TRUE;
  386. }
  387.  
  388. /////////////////
  389. // Handle WM_DRAWITEM on behalf of frame. Note: in all that goes
  390. // below, can't assume rcItem.left=0 because of multi-column menus!
  391. //
  392. BOOL CCoolMenuManager::OnDrawItem(LPDRAWITEMSTRUCT lpds)
  393. {
  394.  
  395.     ASSERT(lpds);
  396.     CMyItemData* pmd = (CMyItemData*)lpds->itemData;
  397.     ASSERT(pmd);
  398.     if (lpds->CtlType != ODT_MENU || !pmd->IsMyItemData())
  399.         return FALSE; // not handled by me
  400.  
  401.     ASSERT(lpds->itemAction != ODA_FOCUS);
  402.     ASSERT(lpds->hDC);
  403.     CDC dc;
  404.     dc.Attach(lpds->hDC);
  405.  
  406.     const CRect& rcItem = lpds->rcItem;
  407.  
  408.     if (pmd->fType & MFT_SEPARATOR)
  409.     {
  410.         // draw separator
  411.         CRect rc = rcItem;                                // copy rect
  412.         rc.top += rc.Height()>>1;                        // vertical center
  413.         dc.DrawEdge(&rc, EDGE_ETCHED, BF_TOP);        // draw separator line
  414.  
  415.     } else 
  416.     {                                                    // not a separator
  417.         CString text = pmd->text;
  418.  
  419.         BOOL bDisabled = lpds->itemState & ODS_GRAYED;
  420.         BOOL bSelected = lpds->itemState & ODS_SELECTED;
  421.         BOOL bChecked  = lpds->itemState & ODS_CHECKED;
  422.         BOOL bHaveButn=FALSE;
  423.         // Paint button, or blank if none
  424.         CRect rcButn(rcItem.TopLeft(), m_szButton);    // button rect
  425.         rcButn += CPoint(0,                                    // center vertically
  426.             (rcItem.Height() - rcButn.Height())>>1 );
  427.         int iButton = pmd->iButton;
  428.  
  429. //TRACE1("text=%s  ",text);TRACE2("on draw: id=%d iButton=%d\n",lpds->itemID,iButton);
  430.         if (iButton >= 0)
  431.         {
  432.  
  433.             // this item has a button!
  434.             bHaveButn = TRUE;
  435.  
  436.             // compute point to start drawing
  437.             CSize sz = rcButn.Size() - m_szButton;
  438.             sz.cx >>= 1;
  439.             sz.cy >>= 1;
  440.             CPoint p(rcButn.TopLeft() + sz);
  441.  
  442.             // draw disabled or normal
  443.             if (!bDisabled) 
  444.             {
  445.                 // normal: fill BG depending on state
  446.                 PLFillRect(dc, rcButn, GetSysColor(
  447.                     (bChecked && !bSelected) ? COLOR_3DLIGHT : COLOR_MENU));
  448.  
  449.                 // draw pushed-in or popped-out edge
  450.                 if (bSelected || bChecked)
  451.                 {
  452.                     CRect rc2 = rcButn;
  453.                     dc.DrawEdge(rc2, bChecked ? BDR_SUNKENOUTER : BDR_RAISEDINNER,
  454.                         BF_RECT);
  455.                 }
  456.             }
  457.             // draw the button!
  458.             DrawButton(dc, iButton, p, !bDisabled);
  459.  
  460.         } else 
  461.         {
  462.             // no button: look for custom checked/unchecked bitmaps
  463.             CMenuItemInfo info;
  464.             info.fMask = MIIM_CHECKMARKS;
  465.             GetMenuItemInfo((HMENU)lpds->hwndItem,
  466.                 lpds->itemID, MF_BYCOMMAND, &info);
  467.             if (bChecked || info.hbmpUnchecked) {
  468.                 bHaveButn = Draw3DCheckmark(dc, rcButn, bSelected,
  469.                     bChecked ? info.hbmpChecked : info.hbmpUnchecked);
  470.         
  471.             }
  472.         }
  473.  
  474.         // Done with button, now paint text. First do background if needed.
  475.         int cxButn = m_szButton.cx;                // width of button
  476.         COLORREF colorBG = GetSysColor(bSelected ? COLOR_HIGHLIGHT : COLOR_MENU);
  477.         if (bSelected || lpds->itemAction==ODA_SELECT)
  478.         {
  479.             // selected or selection state changed: paint text background
  480.             CRect rcBG = rcItem;                            // whole rectangle
  481.             if (bHaveButn)                                    // if there's a button:
  482.                 rcBG.left += cxButn + CXGAP;            //  don't paint over it!
  483.             PLFillRect(dc, rcBG, colorBG);    // paint it!
  484.         }
  485.  
  486.         // compute text rectangle and colors
  487.         CRect rcText = rcItem;                 // start w/whole item
  488.         rcText.left += cxButn + CXGAP + CXTEXTMARGIN; // left margin
  489.         rcText.right -= cxButn;                 // right margin
  490.         dc.SetBkMode(TRANSPARENT);             // paint transparent text
  491.         COLORREF colorText = GetSysColor(bDisabled ?  COLOR_GRAYTEXT :
  492.             bSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT);
  493.  
  494.         // Now paint menu item text.    No need to select font,
  495.         // because windows sets it up before sending WM_DRAWITEM
  496.         //
  497.         if (bDisabled && (!bSelected || colorText == colorBG))
  498.         {
  499.             // disabled: draw hilite text shifted southeast 1 pixel for embossed
  500.             // look. Don't do it if item is selected, tho--unless text color same
  501.             // as menu highlight color. Got it?
  502.             //
  503.             DrawMenuText(dc, rcText + CPoint(1,1), text, GetSysColor(COLOR_3DHILIGHT));
  504.         }
  505.         DrawMenuText(dc, rcText, text, colorText); // finally!
  506.     }
  507.     dc.Detach();
  508.  
  509.     return TRUE; // handled
  510. }
  511.  
  512.  
  513. //////////////////
  514. // Handle WM_INITMENUPOPUP on behalf of frame.
  515. //
  516. void CCoolMenuManager::OnInitMenuPopup(CMenu* pMenu, UINT nIndex, BOOL bSysMenu)
  517. {
  518.     ConvertMenu(pMenu, nIndex, bSysMenu, TRUE);
  519. }
  520.  
  521. //////////////////
  522. // Handle WM_MENUSELECT: check for menu closed
  523. //
  524. void CCoolMenuManager::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu)
  525. {
  526.     if (hSysMenu==NULL && nFlags==0xFFFF) {
  527.         // Windows has closed the menu: restore all menus to original state
  528.         while (!m_menuList.IsEmpty()) {
  529.             ConvertMenu(CMenu::FromHandle((HMENU)m_menuList.RemoveHead()),
  530.                                             0, FALSE, FALSE);
  531.         }
  532.     }
  533. }
  534.  
  535. //////////////////
  536. // This rather gnarly function is used both to convert the menu from strings to
  537. // owner-draw and vice versa. In either case, it also appends automagic
  538. // accelerator key names to the menu items, if m_bAutoAccel is TRUE.
  539. //
  540. void CCoolMenuManager::ConvertMenu(CMenu* pMenu, 
  541.                                    UINT nIndex, BOOL bSysMenu, BOOL bShowButtons)
  542. {
  543.     ASSERT_VALID(pMenu);
  544.  
  545.     CString sItemName;
  546.  
  547.     UINT nItem = pMenu->GetMenuItemCount();
  548.     for (UINT i = 0; i < nItem; i++)  // loop over each item in menu
  549.     {
  550.         // get menu item info
  551.         char itemname[256];
  552.         CMenuItemInfo info;
  553.         info.fMask = MIIM_SUBMENU | MIIM_DATA | MIIM_ID | MIIM_TYPE;
  554.         info.dwTypeData = itemname;
  555.         info.cch = sizeof(itemname);
  556.         ::GetMenuItemInfo(*pMenu, i, TRUE, &info);
  557.         CMyItemData* pmd = (CMyItemData*)info.dwItemData;
  558.  
  559.         if (pmd && !pmd->IsMyItemData())
  560.         {
  561.             TRACE0(_T("CCoolMenuManager: ignoring foreign owner-draw item\n"));
  562.             continue; // owner-draw menu item isn't mine--leave it alone
  563.         }
  564.  
  565.         if (bSysMenu && info.wID >= 0xF000)
  566.         {
  567.             TRACE0(_T("CCoolMenuManager: ignoring sys menu item\n"));
  568.             continue; // don't do for system menu commands
  569.         }
  570.     
  571.         // now that I have the info, I will modify it
  572.         info.fMask = 0;    // assume nothing to change
  573.  
  574.         if (bShowButtons)
  575.         {
  576.  
  577.             // I'm showing buttons: convert to owner-draw
  578.  
  579.             if (!(info.fType & MFT_OWNERDRAW))
  580.             {
  581.                 // If not already owner-draw, make it so. NOTE: If app calls
  582.                 // pCmdUI->SetText to change the text of a menu item, MFC will
  583.                 // turn the item to MFT_STRING. So I must set it back to
  584.                 // MFT_OWNERDRAW again. In this case, the menu item data (pmd)
  585.                 // will still be there.
  586.                 // 
  587.                 info.fType |= MFT_OWNERDRAW;
  588.                 info.fMask |= MIIM_TYPE;
  589.                 if (!pmd)
  590.                 {                                        // if no item data:
  591.                     pmd = new CMyItemData;                //   create one
  592.                     ASSERT(pmd);                        //   (I hope)
  593.                     m_menuItems.AddTail(pmd);
  594.  
  595.                     pmd->fType = info.fType;            //   handy when drawing
  596.                     pmd->iButton=m_pToolBar->CommandToIndex(info.wID);
  597.  
  598.  
  599. //TRACE2(_T("new wId=%d %s \t"), info.wID, info.dwTypeData); TRACE1("nin=%d\n",++nin);
  600.                     info.dwItemData = (DWORD)pmd;        //   set in menu item data
  601.                     info.fMask |= MIIM_DATA;            //   set item data
  602.                 }
  603.                 pmd->text = info.dwTypeData;            // copy menu item string
  604.             }
  605.  
  606.             // now add the menu to list of "converted" menus
  607.             HMENU hmenu = pMenu->GetSafeHmenu();
  608.             ASSERT(hmenu);
  609.             if (!m_menuList.Find(hmenu))
  610.                 m_menuList.AddHead(hmenu);
  611.  
  612.  
  613.         } else // !bShowButtons
  614.         {
  615.  
  616.             // no buttons -- I'm converting to strings
  617.             
  618.             if (info.fType & MFT_OWNERDRAW)
  619.             {                                        // if ownerdraw:
  620.                 info.fType &= ~MFT_OWNERDRAW;        //   turn it off
  621.                 info.fMask |= MIIM_TYPE;            //   change item type
  622.                 ASSERT(pmd);                        //   sanity check
  623.                 sItemName = pmd->text;                //   save name before deleting pmd
  624.             } else                                    // otherwise:
  625.                 sItemName = info.dwTypeData;        //   use name from MENUITEMINFO
  626.  
  627.             if (pmd) 
  628.             {
  629.                 // NOTE: pmd (item data) could still be left hanging around even
  630.                 // if MFT_OWNERDRAW is not set, in case mentioned above where app
  631.                 // calls pCmdUI->SetText to set text of item and MFC sets the type
  632.                 // to MFT_STRING.
  633.                 //
  634.                 info.dwItemData = NULL;                // item data is NULL
  635.                 info.fMask |= MIIM_DATA;            // change it
  636.                 POSITION pos=m_menuItems.Find(pmd);
  637.                 if (pos!=NULL)
  638.                     m_menuItems.RemoveAt(pos);
  639.                 delete pmd;                                // and item data too
  640. //TRACE2(_T("delete wId=%d %s \t"), info.wID, sItemName); TRACE1("nin=%d\n",--nin);
  641.             }
  642.  
  643.                 
  644.             if (info.fMask & MIIM_TYPE)
  645.             {
  646.                 // if setting name, copy name from CString to buffer and set cch
  647.                 strncpy(itemname, sItemName, sizeof(itemname));
  648.                 info.dwTypeData = itemname;
  649.                 info.cch = sItemName.GetLength();
  650.             }
  651.         }
  652.  
  653.         // if after all the above, there is anything to change, change it
  654.         if (info.fMask)
  655.         {
  656.             //TRACE2(_T("Converting '%s' to %s\n"), itemname,    (info.fType & MFT_OWNERDRAW) ? _T("OWNERDRAW") : _T("STRING"));
  657.             SetMenuItemInfo(*pMenu, i, TRUE, &info);
  658.         }
  659.     }
  660. }
  661.  
  662.  
  663.  
  664.  
  665. //////////////////
  666. // Virtual CSubclassWnd window proc. All messages come here before frame
  667. // window. Isn't it cool? Just like in the old days!
  668. //
  669. LRESULT CCoolMenuManager::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
  670. {
  671.     switch(msg)
  672.     {
  673.  
  674.         case WM_MEASUREITEM:
  675.             if (OnMeasureItem((MEASUREITEMSTRUCT*)lp))
  676.                 return TRUE; // handled
  677.             break;
  678.  
  679.         case WM_DRAWITEM:
  680.             if (OnDrawItem((DRAWITEMSTRUCT*)lp))
  681.                 return TRUE; // handled
  682.             break;
  683.  
  684.         case WM_INITMENUPOPUP:
  685.             // Very important: must let frame window handle it first!
  686.             // Because if someone calls CCmdUI::SetText, MFC will change item to
  687.             // MFT_STRING, so I must change back to MFT_OWNERDRAW.
  688.             //
  689.             CSubclassWnd::WindowProc(msg, wp, lp);
  690.             OnInitMenuPopup(CMenu::FromHandle((HMENU)wp),
  691.                 (UINT)LOWORD(lp), (BOOL)HIWORD(lp));
  692.             return 0;
  693.  
  694.         case WM_MENUSELECT:
  695.             OnMenuSelect((UINT)LOWORD(wp), (UINT)HIWORD(wp), (HMENU)lp);
  696.             break;
  697.  
  698.         case WM_NOTIFY: 
  699.             OnNotify(msg, wp, lp);    
  700.                 break;
  701.         case WM_COMMAND:
  702.             {
  703.                 UINT nId = LOWORD(wp);// GET_WM_COMMAND_ID(wParam,lParam);
  704.                 switch ( nId )
  705.                 {
  706.                     case ID_FILEDROPDOWN: //-->> unused now
  707.                          OnFiledropdown(nId);
  708.                          break;
  709.                 }
  710.                 break;
  711.             }
  712.     }
  713.     return CSubclassWnd::WindowProc(msg, wp, lp);
  714. }
  715.  
  716.  
  717.  
  718. BOOL CCoolMenuManager::OnNotify(UINT msg, WPARAM wParam, LPARAM lParam)
  719. {
  720.     LPNMHDR lpnm = (LPNMHDR)lParam;
  721.  
  722.     switch(lpnm->code)
  723.     {
  724.           case TBN_DROPDOWN:
  725.           {
  726.  
  727.             RECT    rc;
  728.             LPNMTOOLBAR lpnmTB =(LPNMTOOLBAR)lParam;
  729.  
  730.             CMyDropData* pdd;
  731.             ASSERT(pdd);
  732.             if (   !m_dropButns.Lookup(lpnmTB->iItem, (void *&)pdd)
  733.                 || !pdd->IsMyDropData())
  734.             {
  735. //                TRACE2("idBut=%d idMenu=%d\n",lpnmTB->iItem, pdd->idMenu);
  736.                 return FALSE; // not handled by me
  737.             }
  738.             
  739.             SendMessage(lpnmTB->hdr.hwndFrom, TB_GETRECT,
  740.                         (WPARAM)lpnmTB->iItem, (LPARAM)&rc);
  741.             MapWindowPoints(lpnmTB->hdr.hwndFrom,  HWND_DESKTOP, (LPPOINT)&rc, 2);                         
  742.  
  743.             CMenu bar;
  744.             if (bar.LoadMenu(pdd->idMenu))
  745.             {
  746.                 CMenu& popup = *bar.GetSubMenu(0);
  747.                 ASSERT(popup.m_hMenu != NULL);
  748.                 popup.TrackPopupMenu(pdd->uFlags, rc.left, rc.bottom, AfxGetMainWnd());
  749.             }
  750.  
  751.  
  752. /*
  753.  
  754.          TPMPARAMS tpm;
  755.          tpm.cbSize = sizeof(TPMPARAMS);
  756.          tpm.rcExclude = rc;
  757.          hMenuLoaded = LoadMenu(g_hinst, MAKEINTRESOURCE(IDR_POPUP)); 
  758.          hPopupMenu = GetSubMenu(LoadMenu(g_hinst,
  759.             MAKEINTRESOURCE(IDR_POPUP)),0);
  760.          TrackPopupMenuEx(hPopupMenu,
  761.             TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL,               
  762.             rc.left, rc.bottom, g_hwndMain, &tpm); 
  763.          DestroyMenu(hMenuLoaded);            
  764. */
  765.  
  766.           }
  767.  
  768.       return (FALSE);
  769.    }
  770.  
  771.    return FALSE;
  772. }
  773.  
  774.  
  775.  
  776.  
  777. //-------------------------------------------------------------------------------
  778. // ╧ε Ωφε∩Ωσ popup menu
  779. // Unused now
  780. void CCoolMenuManager::OnFiledropdown(UINT nIdButn) 
  781. {
  782.     UINT nIdMenu;
  783.     if  (nIdButn == ID_FILEDROPDOWN) 
  784.          nIdMenu = IDR_FILEDROPDOWN;
  785.     else return;
  786.     CRect rc;
  787.     m_pToolBar->GetToolBarCtrl().GetRect(nIdButn, &rc);
  788.     
  789.  
  790.     CPoint point(rc.left, rc.bottom+3);
  791.     m_pFrame->ClientToScreen(&point);
  792.     if (!m_pToolBar->SendMessage(TB_ISBUTTONCHECKED,(WPARAM)nIdButn,0))
  793.     {
  794.         CMenu bar;
  795.         if (bar.LoadMenu(nIdMenu))
  796.         {
  797.             m_pToolBar->SendMessage(TB_CHECKBUTTON,(WPARAM)nIdButn,(LPARAM)MAKELONG( TRUE,0));
  798.             CMenu& popup = *bar.GetSubMenu(0);
  799.             ASSERT(popup.m_hMenu != NULL);
  800.  
  801.             popup.TrackPopupMenu(TPM_RIGHTBUTTON, point.x, point.y,    AfxGetMainWnd());
  802.         }
  803.     }
  804.     m_pToolBar->SendMessage(TB_CHECKBUTTON,(WPARAM)nIdButn,(LPARAM)MAKELONG(FALSE,0));
  805. }
  806.